java高并发秒杀活动的各种简单实现【springBoot+mybatis+redis+mysql】

您所在的位置:网站首页 java 并发处理xml java高并发秒杀活动的各种简单实现【springBoot+mybatis+redis+mysql】

java高并发秒杀活动的各种简单实现【springBoot+mybatis+redis+mysql】

2024-07-06 13:09| 来源: 网络整理| 查看: 265

最近遇到比较多数据不一致的问题,大多数都是因为并发请求时,没及时处理的原因,故用一个比较有代表性的业务场景【活动秒杀】来模拟一下这个这种高并发所产生的问题。

首先搭建一个springboot项目在这里我做演示了,不会的可以自行百度,搭建过程很简单。

1:搭建好的项目目录结构

 

2:商品表(记录商品名称,本次可以秒杀的库存量)

 

加了一条记录(后面每次测试都先手动把库存恢复成100才进行测试)

 

 

3:实体类(这里不用实体类也可以,根据自己的需求来)

 

一、不做任何处理的高并发秒杀实现(错误演示):

1.Controller层,模拟500个并发调用:

package com.mybatis.controller; import com.mybatis.domain.BaseResponse;import com.mybatis.domain.MiaoshaRequest;import com.mybatis.service.MiaoshaService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;   @Controller@RequestMapping(value="/miaoshagoods")public class MiaoshaController {     @Autowired    public MiaoshaService miaoshaService;      @PostMapping("/miaosha_java_sql_lock")    public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){         BaseResponse response=new BaseResponse();            for(int i=0;i0){            miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);            countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);        }        if(countSuc==1){            System.out.println("抢到iphoneX,成功!");        }else{            System.out.println("抢到iphoneX,失败!");        }        return response;    }}3.dao层(mybatis的xml文件):

     select        from miao_sha_goods    where goods_name = #{goodsName,jdbcType=VARCHAR}         update miao_sha_goods    set goods_sum = #{goodsSum,jdbcType=INTEGER}    where goods_name = #{goodsName,jdbcType=VARCHAR} 

 4.测试结果:

 

 

 

 

 

 

 截图表明,居然有500个人抢购成功,而且库存量却只减少了12个,这是明显是错误的。

 

二、数据库乐观锁处理的高并发秒杀实现:

 

package com.mybatis.controller; import com.mybatis.domain.BaseResponse;import com.mybatis.domain.MiaoshaRequest;import com.mybatis.service.MiaoshaService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;  @Controller@RequestMapping(value="/miaoshagoods")public class MiaoshaController {     @Autowired    public MiaoshaService miaoshaService;     @PostMapping("/miaosha_java_sql_lock")    public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){         BaseResponse response=new BaseResponse();        for(int i=0;i0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");            }else{                System.out.println("抢到iphoneX,失败!");            }            return response;        }        /**         * 数据库乐观锁实现秒杀         * @param request         * @return         */        @Override        public BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) {            int countSuc=0;            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames());            if(miaoShaGoods.getGoodsSum()>0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");                     //做出相应的逻辑(记录抢购成功的用户名什么的....)                 }else{                System.out.println("抢到iphoneX,失败!");                     //重试或者返回友好的提示什么的....            }            return response;        }    }

3.dao层(mybatis的xml文件)[在SQL层面改为数据库乐观锁]:

            select                from miao_sha_goods        where goods_name = #{goodsName,jdbcType=VARCHAR}                         update miao_sha_goods        set goods_sum = #{goodsSum,jdbcType=INTEGER},version=version+1        where goods_name = #{goodsName,jdbcType=VARCHAR} and version = #{version,jdbcType=VARCHAR}     

4.测试结果:

 

 截图表明,总共有500个人抢,有29个人抢购成功,而且库存量减少了29个,这保证了库存的正确性。但却会有抢购不成功的请求,需要我们后续去处理。

三、数据库悲观锁处理的高并发秒杀实现:

1.Controller层,模拟500个并发调用:

    package com.mybatis.controller;         import com.mybatis.domain.BaseResponse;    import com.mybatis.domain.MiaoshaRequest;    import com.mybatis.service.MiaoshaService;    import org.springframework.beans.factory.annotation.Autowired;    import org.springframework.data.redis.core.RedisTemplate;    import org.springframework.stereotype.Controller;    import org.springframework.web.bind.annotation.*;              @Controller    @RequestMapping(value="/miaoshagoods")    public class MiaoshaController {             @Autowired        public MiaoshaService miaoshaService;             @PostMapping("/miaosha_java_sql_lock")        public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){                 BaseResponse response=new BaseResponse();            for(int i=0;i0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");            }else{                System.out.println("抢到iphoneX,失败!");            }            return response;        }        /**         * 数据库乐观锁实现秒杀         * @param request         * @return         */        @Override        public BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) {            int countSuc=0;            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames());            if(miaoShaGoods.getGoodsSum()>0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");                     //做出相应的逻辑(记录抢购成功的用户名什么的....)                 }else{                System.out.println("抢到iphoneX,失败!");                     //重试或者返回友好的提示什么的....            }            return response;        }        /**         * 数据库悲观锁实现秒杀         * @param request         * @return         */        @Override        @Transactional        public BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) {            int countSuc=0;            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames());            if(miaoShaGoods.getGoodsSum()>0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");            }else{                System.out.println("抢到iphoneX,失败!");            }            return response;        }    }

 3.dao层(mybatis的xml文件)[在SQL层面改为数据库悲观锁]:

              select                from miao_sha_goods        where goods_name = #{goodsName,jdbcType=VARCHAR} FOR UPDATE                         update miao_sha_goods        set goods_sum = #{goodsSum,jdbcType=INTEGER}        where goods_name = #{goodsName,jdbcType=VARCHAR}     

4.测试结果:

 

 

 

截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性。

 

四、java线程同步锁处理的高并发秒杀实现:

1.Controller层,模拟500个并发调用:

    package com.mybatis.controller;         import com.mybatis.domain.BaseResponse;    import com.mybatis.domain.MiaoshaRequest;    import com.mybatis.service.MiaoshaService;    import org.springframework.beans.factory.annotation.Autowired;    import org.springframework.data.redis.core.RedisTemplate;    import org.springframework.stereotype.Controller;    import org.springframework.web.bind.annotation.*;              @Controller    @RequestMapping(value="/miaoshagoods")    public class MiaoshaController {             @Autowired        public MiaoshaService miaoshaService;             @PostMapping("/miaosha_java_sql_lock")        public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){                 BaseResponse response=new BaseResponse();            for(int i=0;i0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");            }else{                System.out.println("抢到iphoneX,失败!");            }            return response;        }        /**         * 数据库乐观锁实现秒杀         * @param request         * @return         */        @Override        public BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) {            int countSuc=0;            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames());            if(miaoShaGoods.getGoodsSum()>0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");                     //做出相应的逻辑(记录抢购成功的用户名什么的....)                 }else{                System.out.println("抢到iphoneX,失败!");                     //重试或者返回友好的提示什么的....            }            return response;        }        /**         * 数据库悲观锁实现秒杀         * @param request         * @return         */        @Override        @Transactional        public BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) {            int countSuc=0;            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames());            if(miaoShaGoods.getGoodsSum()>0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");            }else{                System.out.println("抢到iphoneX,失败!");            }            return response;        }        /**         * java同步锁实现秒杀         * @param request         * @return         */        @Override        public BaseResponse miaoshaGoods_java_synchronized_lock(MiaoshaRequest request,BaseResponse response) {            synchronized(this){                int countSuc=0;                MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());                if(miaoShaGoods.getGoodsSum()>0){                    miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                    countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);                }                if(countSuc==1){                    System.out.println("抢到iphoneX,成功!");                }else{                    System.out.println("抢到iphoneX,失败!");                }            }            return response;        }    }

3.dao层(mybatis的xml文件):

              select                from miao_sha_goods        where goods_name = #{goodsName,jdbcType=VARCHAR}                         update miao_sha_goods        set goods_sum = #{goodsSum,jdbcType=INTEGER}        where goods_name = #{goodsName,jdbcType=VARCHAR}     

4.测试结果:

 

 

 

 截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性。

 五、java线程可重入锁处理的高并发秒杀实现:

1.Controller层,模拟500个并发调用:

    package com.mybatis.controller;         import com.mybatis.domain.BaseResponse;    import com.mybatis.domain.MiaoshaRequest;    import com.mybatis.service.MiaoshaService;    import org.springframework.beans.factory.annotation.Autowired;    import org.springframework.data.redis.core.RedisTemplate;    import org.springframework.stereotype.Controller;    import org.springframework.web.bind.annotation.*;              @Controller    @RequestMapping(value="/miaoshagoods")    public class MiaoshaController {             @Autowired        public MiaoshaService miaoshaService;             @PostMapping("/miaosha_java_sql_lock")        public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){                 BaseResponse response=new BaseResponse();            for(int i=0;i0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");            }else{                System.out.println("抢到iphoneX,失败!");            }            return response;        }        /**         * 数据库乐观锁实现秒杀         * @param request         * @return         */        @Override        public BaseResponse miaoshaGoods_sql_optimistic_lock(MiaoshaRequest request,BaseResponse response) {            int countSuc=0;            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_lgs(request.getGoodNames());            if(miaoShaGoods.getGoodsSum()>0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");                     //做出相应的逻辑(记录抢购成功的用户名什么的....)                 }else{                System.out.println("抢到iphoneX,失败!");                     //重试或者返回友好的提示什么的....            }            return response;        }        /**         * 数据库悲观锁实现秒杀         * @param request         * @return         */        @Override        @Transactional        public BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) {            int countSuc=0;            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames());            if(miaoShaGoods.getGoodsSum()>0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");            }else{                System.out.println("抢到iphoneX,失败!");            }            return response;        }        /**         * java同步锁实现秒杀         * @param request         * @return         */        @Override        public BaseResponse miaoshaGoods_java_synchronized_lock(MiaoshaRequest request,BaseResponse response) {            synchronized(this){                int countSuc=0;                MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());                if(miaoShaGoods.getGoodsSum()>0){                    miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                    countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);                }                if(countSuc==1){                    System.out.println("抢到iphoneX,成功!");                }else{                    System.out.println("抢到iphoneX,失败!");                }            }            return response;        }        /**         * java可重入锁实现秒杀         * @param request         * @return         */        @Override        public BaseResponse miaoshaGoods_java_reentrant_lock(MiaoshaRequest request,BaseResponse response) {                 lock.lock();            int countSuc=0;            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());            if(miaoShaGoods.getGoodsSum()>0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);            }            if(countSuc==1){                System.out.println(request.getGoodNames()+"抢到iphoneX,成功!");            }else{                System.out.println("抢到iphoneX,失败!");            }            lock.unlock();            return response;        }    }

3.dao层(mybatis的xml文件):

              select                from miao_sha_goods        where goods_name = #{goodsName,jdbcType=VARCHAR}                         update miao_sha_goods        set goods_sum = #{goodsSum,jdbcType=INTEGER}        where goods_name = #{goodsName,jdbcType=VARCHAR}     

4.测试结果:

 

 

 

  截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性。

六、redis单线程处理的高并发秒杀实现(推荐):

1.Controller层,模拟500个并发调用:

    package com.mybatis.controller;         import com.mybatis.domain.BaseResponse;    import com.mybatis.domain.MiaoshaRequest;    import com.mybatis.service.MiaoshaService;    import org.springframework.beans.factory.annotation.Autowired;    import org.springframework.data.redis.core.RedisTemplate;    import org.springframework.stereotype.Controller;    import org.springframework.web.bind.annotation.*;                   @Controller    @RequestMapping(value="/miaoshagoods")    public class MiaoshaController {             @Autowired        public MiaoshaService miaoshaService;             @Autowired        private RedisTemplate redisTemplate;                  @PostMapping("/miaosha_java_sql_lock")        public @ResponseBody BaseResponse miaoshaJavaSqlLock(@RequestBody MiaoshaRequest request){                 BaseResponse response=new BaseResponse();            for(int i=0;i0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods_lgs(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");                     //做出相应的逻辑(记录抢购成功的用户名什么的....)                 }else{                System.out.println("抢到iphoneX,失败!");                     //重试或者返回友好的提示什么的....            }            return response;        }        /**         * 数据库悲观锁实现秒杀         * @param request         * @return         */        @Override        @Transactional        public BaseResponse miaoshaGoods_sql_pessimistic_lock(MiaoshaRequest request,BaseResponse response) {            int countSuc=0;            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods_bgs(request.getGoodNames());            if(miaoShaGoods.getGoodsSum()>0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods_bgs(miaoShaGoods);            }            if(countSuc==1){                System.out.println("抢到iphoneX,成功!");            }else{                System.out.println("抢到iphoneX,失败!");            }            return response;        }        /**         * java同步锁实现秒杀         * @param request         * @return         */        @Override        public BaseResponse miaoshaGoods_java_synchronized_lock(MiaoshaRequest request,BaseResponse response) {            synchronized(this){                int countSuc=0;                MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());                if(miaoShaGoods.getGoodsSum()>0){                    miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                    countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);                }                if(countSuc==1){                    System.out.println("抢到iphoneX,成功!");                }else{                    System.out.println("抢到iphoneX,失败!");                }            }            return response;        }        /**         * java可重入锁实现秒杀         * @param request         * @return         */        @Override        public BaseResponse miaoshaGoods_java_reentrant_lock(MiaoshaRequest request,BaseResponse response) {                 lock.lock();            int countSuc=0;            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());            if(miaoShaGoods.getGoodsSum()>0){                miaoShaGoods.setGoodsSum(miaoShaGoods.getGoodsSum()-1);                countSuc= miaoShaGoodsDao.updateMsGoods(miaoShaGoods);            }            if(countSuc==1){                System.out.println(request.getGoodNames()+"抢到iphoneX,成功!");            }else{                System.out.println("抢到iphoneX,失败!");            }            lock.unlock();            return response;        }             @Override        public Integer getGoodsSum(MiaoshaRequest request) {            MiaoShaGoods miaoShaGoods=miaoShaGoodsDao.getGoods(request.getGoodNames());            return miaoShaGoods.getGoodsSum();        }        /**         * redis实现秒杀         * @param response         * @return         */        @Override        public BaseResponse miaoshaGoods_redis(MiaoshaRequest request,BaseResponse response) {                 //增量计算剩余库存(利用redis的单线程特性)            double goodsSurplusSum=redisTemplate.opsForValue().increment(request.getGoodNames()+":goodsSum",-1);            if(goodsSurplusSum>=0){                System.out.println("抢到iphoneX,成功!还剩下:"+goodsSurplusSum);            }else {                System.out.println("抢到iphoneX,失败!");            }            return response;        }    }3.测试结果:

 

 

 

 

 

 

 截图表明,总共有500个人抢,有100个人抢购成功,而且库存量减少了100个,这保证了库存的正确性,抢后redis显示是-400个,说明有500个人进来了,有400个人抢不到。综上所述,要控制库存量,一般要用锁机制。但是一般加锁的话会比较影响性能(只能用于单服务),推荐大家使用redis自带的线程安全属性来实现(可实现分布式锁)。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3